package cn.seaboot.commons.ftp;

import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPReply;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;

/**
 * ftp 数据源
 * <p>
 * 本身是一个 DataSource，提供了 FTPClient 的创建、有效性验证等功能，
 * 1 个 {@link FtpSource} 对应于 1 个 {@link FtpProperties}，
 * 如果一个项目中维护了很多的 ftp 服务，则需要创建很多的 {@link FtpSource}。
 *
 * @author Mr.css
 * @version 2021-02-20 11:04
 */
public class FtpSource implements DataSource {
    private final Logger logger = LoggerFactory.getLogger(FtpSource.class);

//     public static void main(String[] args) {
//         FtpProperties ftpProperties = new FtpProperties();
//         ftpProperties.setHost("180.76.136.213");
//         ftpProperties.setPassword("ftp-1993");
//         ftpProperties.setUsername("datauser");
//
//         FtpClient ftpClient = new FtpClient(ftpProperties);
//     }

    /**
     * FTP参数
     */
    private final FtpProperties ftpProperties;

    public FtpSource(FtpProperties config) {
        this.ftpProperties = config;
    }

    /**
     * return FtpProperties
     *
     * @return FtpProperties
     */
    public FtpProperties getFtpProperties() {
        return ftpProperties;
    }

    /**
     * 返回一个不受连接池管理的FtpCommand
     *
     * @return FtpCommand
     */
    @Override
    public Ftp build() {
        return new Ftp(this, this.ftpProperties);
    }

    /**
     * 创建FtpClient对象，与{@link #closeFTPClient(FTPClient)}对应，用完需要自己销毁，不受连接池管理
     */
    @Override
    public FTPClient createFTPClient() throws IOException {
        FTPClient ftpClient = new FTPClient();
        ftpClient.setConnectTimeout(ftpProperties.getConnectTimeout());
        ftpClient.connect(ftpProperties.getHost(), ftpProperties.getPort());
        ftpClient.setBufferSize(ftpProperties.getBufferSize());
        ftpClient.setFileType(ftpProperties.getTransferFileType());

        // 登录
        if (ftpProperties.getPassword() != null) {
            if (!ftpClient.login(ftpProperties.getUsername(), ftpProperties.getPassword())) {
                throw new FtpException("FTPServer login failed, username or password is incorrect!");
            }
        }

        // 确定是否连接成功
        logger.debug("Receive a ftp connection: " + ftpClient);
        int replyCode = ftpClient.getReplyCode();
        if (!FTPReply.isPositiveCompletion(replyCode)) {
            ftpClient.disconnect();
            throw new FtpException("FTPServer refused connection, replyCode: " + replyCode);
        }

        // 确定字符编码，主要解决中文文件名乱码问题
        String encoding = ftpProperties.getEncoding();
        if (encoding == null) {
            if (FTPReply.isPositiveCompletion(ftpClient.sendCommand("OPTS UTF8", "ON"))) {
                encoding = "UTF-8";
            } else {
                encoding = "GBK";
            }
        }
        ftpProperties.setEncoding(encoding);
        ftpClient.setControlEncoding(encoding);

        // 确定主动模式还是被动模式
        if (ftpProperties.isPassiveMode()) {
            ftpClient.enterLocalPassiveMode();
        }
        return ftpClient;
    }

    /**
     * 关闭一个FTP连接
     *
     * @param ftp -
     */
    @Override
    public void closeFtp(Ftp ftp) {
        this.closeFTPClient(ftp.getFtpClient());
    }

    /**
     * 验证ftpClient的有效性
     *
     * @param ftpClient -
     * @return true/false
     */
    public boolean validateFtpClient(FTPClient ftpClient) {
        try {
            if (ftpClient.isConnected()) {
                // Try to connect to ftp server, make sure that connect is succeed. ftpClient.printWorkingDirectory();
                return ftpClient.sendNoOp();
            }
        } catch (IOException e) {
            logger.error("Ftp client is failure:", e);
        }
        return false;
    }

    /**
     * 尝试退出登录之后，销毁一个连接
     *
     * @param ftpClient -
     */
    public void closeFTPClient(FTPClient ftpClient) {
        logger.debug("Destroy a connection :{}", ftpClient);
        try {
            if (ftpClient.isConnected()) {
                ftpClient.logout();
            }
        } catch (IOException io) {
            logger.error("Ftp client logout failed: ", io);
        } finally {
            try {
                ftpClient.disconnect();
            } catch (IOException io) {
                logger.error("Close ftp client failed: ", io);
            }
        }
    }
}
